package javapop.framework.parser;

import java.util.ArrayList;
import java.util.List;

import javapop.framework.ParseContext;
import javapop.framework.ParseError;
import javapop.framework.ParseInput;
import javapop.framework.ParseResult;
import javapop.framework.Parser;

public class Choice<T> extends Parser<T> {
	private List<Parser<T>> children;
	private String errorMessage;
	private String description;
	
	public Choice() {
		children = new ArrayList<Parser<T>>();
		errorMessage = null;
		description = null;
	}
	
	public Choice<T> either(Parser<T> first) {
		if(!children.isEmpty()) {
			throw new IllegalStateException("Choice is not empty");
		}
		children.add(first);
		return this;
	}
	
	public Choice<T> orElse(Parser<T> next) {
		if(children.isEmpty()) {
			throw new IllegalStateException("Choice is empty");
		}
		children.add(next);
		return this;
	}
	
	@Override
	public boolean isMemoized() {
		return false;
	}
	
	@Override
	public String getDescription() {
		if(description==null) {
			StringBuffer buf = new StringBuffer();
			for(int i=0;i<children.size();i++) {
				buf.append(children.get(i).getDescription());
				if(i<children.size()) {
					buf.append(" | ");
				}
			}
			description = buf.toString();
		}
		return description;
	}
	
	private String getErrorMessage() {
		if(errorMessage==null) {
			StringBuilder buf = new StringBuilder();
			buf.append("Expecting ");
			for(int i=0;i<children.size();i++) {
				buf.append(children.get(i).getDescription());
				if(i<children.size()) {
					buf.append(", or ");
				}
			}
			errorMessage = buf.toString();
		}
		return errorMessage;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ParseResult<T> eval(ParseContext<?> ctx, ParseInput input) {
		for(Parser<T> child : children) {
			ParseResult<T> result = child.parse(ctx,input);
			if(!result.isError()) {
				return result;
			} else if(result.asError().isFatal()) {
				return result;
			}
		}
		return (ParseResult<T>) new ParseError(getErrorMessage());
		 
	}
			
}
